﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using MARZ_Script.analysis;
using MARZ_Script.lexer;
using MARZ_Script.node;
using MARZ_Script.parser;
using SableCC15;
using System.Text.RegularExpressions;

namespace SableCC15.CodeGeneration
{
    class ExpressionCodeGen : DepthFirstAdapter
    {
        private string _operation;
        private bool ifCall = false;
        public ExpressionCodeGen() { }
        public ExpressionCodeGen(bool ifCall)
        {
            this.ifCall = ifCall;
        }

        #region assign
        public override void CaseAAssignExpression(AAssignExpression node)
        {
            if (node.GetExpression().NodeType == "coord")
            {
                Utility.Emit(node.GetScalar().ToString().Trim() + ".X = ");
                node.GetExpression().Apply(new CoordXCodeGen());
                Utility.Emit(";");
                Utility.Emit(node.GetScalar().ToString().Trim() + ".Y = ");
                node.GetExpression().Apply(new CoordYCodeGen());               
            }
            else
            {
                node.GetScalar().Apply(this);
                Utility.Emit("=");
                node.GetExpression().Apply(this);
            }
        }

        public override void CaseAAssignAddExpression(AAssignAddExpression node)
        {
            if (node.GetScalar().NodeType == "coord")
            {
                Utility.Emit(node.GetScalar().ToString().Trim() + ".X");
                Utility.Emit(node.GetAsignAdd().Text.Trim());
                node.GetExpression().Apply(new CoordXCodeGen());
                Utility.Emit(";");
                Utility.Emit(node.GetScalar().ToString().Trim() + ".Y");
                Utility.Emit(node.GetAsignAdd().Text.Trim());
                node.GetExpression().Apply(new CoordYCodeGen());                
            }
            else
            {
                node.GetScalar().Apply(this);
                Utility.Emit("+="); //code for assign
                node.GetExpression().Apply(this); //prints the right side of the expression
            }
        }

        public override void CaseAAssignMultExpression(AAssignMultExpression node)
        {
            if (node.GetScalar().NodeType == "coord")
            {
                Utility.Emit(node.GetScalar().ToString().Trim() + ".X");
                Utility.Emit(node.GetAsignMult().Text.Trim());
                node.GetExpression().Apply(new CoordXCodeGen());
                Utility.Emit(";");
                Utility.Emit(node.GetScalar().ToString().Trim() + ".Y");
                Utility.Emit(node.GetAsignMult().Text.Trim());
                node.GetExpression().Apply(new CoordYCodeGen());
            }
            else
            {
                node.GetScalar().Apply(this);
                Utility.Emit("*="); //code for assign
                node.GetExpression().Apply(this); //prints the right side of the expression
            }
        }
        public override void CaseAAssignSubExpression(AAssignSubExpression node)
        {
            if (node.GetScalar().NodeType == "coord")
            {
                Utility.Emit(node.GetScalar().ToString().Trim() + ".X");
                Utility.Emit(node.GetAsignSub().Text.Trim());
                node.GetExpression().Apply(new CoordXCodeGen());
                Utility.Emit(";");
                Utility.Emit(node.GetScalar().ToString().Trim() + ".Y");
                Utility.Emit(node.GetAsignSub().Text.Trim());
                node.GetExpression().Apply(new CoordYCodeGen());
            }
            else
            {
                node.GetScalar().Apply(this);
                Utility.Emit("-="); //code for assign
                node.GetExpression().Apply(this); //prints the right side of the expression     
            }
        }

        public override void CaseAAssignDivideExpression(AAssignDivideExpression node)
        {
            if (node.GetScalar().NodeType == "coord")
            {
                Utility.Emit(node.GetScalar().ToString().Trim() + ".X");
                Utility.Emit(node.GetAsignDivide().Text.Trim());
                node.GetExpression().Apply(new CoordXCodeGen());
                Utility.Emit(";");
                Utility.Emit(node.GetScalar().ToString().Trim() + ".Y");
                Utility.Emit(node.GetAsignDivide().Text.Trim());
                node.GetExpression().Apply(new CoordYCodeGen());
                return;
            }
            else
            {

                //base.InAAssignExpression(node); vi gide ikke lave bottom op gennem gang her
                node.GetScalar().Apply(this);
                Utility.Emit("="); //code for assign
                node.GetScalar().Apply(this);
                Utility.Emit("/(");
                node.GetExpression().Apply(this); //prints the right side of the expression            
                Utility.Emit(")");
            }
        }
        public override void CaseAAssignModExpression(AAssignModExpression node)
        {
            //base.InAAssignExpression(node); vi gide ikke lave bottom op gennem gang her
            node.GetScalar().Apply(this);
            Utility.Emit("="); //code for assign
            node.GetScalar().Apply(this);
            Utility.Emit("%(");
            node.GetExpression().Apply(this); //prints the right side of the expression            
            Utility.Emit(")");
        }
        #endregion
        public override void CaseACondConditionalExp(ACondConditionalExp node)
        {
            node.GetL().Apply(this);
            Utility.Emit(node.GetOpIf().Text);
            node.GetR().Apply(this);
            Utility.Emit(node.GetOpDo().Text);
            node.GetConditionalExp().Apply(this);
        }

        public override void CaseAOrOrExp(AOrOrExp node)
        {
            node.GetOrExp().Apply(this);
            Utility.Emit("||");
            node.GetAndExp().Apply(this);
        }

        public override void CaseAAndAndExp(AAndAndExp node)
        {
            node.GetAndExp().Apply(this);
            Utility.Emit("&&");
            node.GetEqualityExp().Apply(this);
        }

        #region equal
        public override void CaseAEqualEqualityExp(AEqualEqualityExp node)
        {
            node.GetEqualityExp().Apply(this);
            Utility.Emit(node.GetBoolEqual().Text);
            node.GetCompareExp().Apply(this);
        }
        public override void CaseANotEqualEqualityExp(ANotEqualEqualityExp node)
        {
            node.GetEqualityExp().Apply(this);
            Utility.Emit(node.GetBoolNotEqual().Text);
            node.GetCompareExp().Apply(this);
        }
        #endregion

        #region compare
        public override void CaseALesserCompareExp(ALesserCompareExp node)
        {
            node.GetCompareExp().Apply(this);
            Utility.Emit(node.GetBoolLesser().Text);
            node.GetAddExp().Apply(this);
        }
        public override void CaseAGreaterCompareExp(AGreaterCompareExp node)
        {
            node.GetCompareExp().Apply(this);
            Utility.Emit(node.GetBoolGreater().Text);
            node.GetAddExp().Apply(this);
        }
        public override void CaseAEqualOrLCompareExp(AEqualOrLCompareExp node)
        {
            node.GetCompareExp().Apply(this);
            Utility.Emit(node.GetBoolEqualOrL().Text);
            node.GetAddExp().Apply(this);
        }
        public override void CaseAEqualOrGCompareExp(AEqualOrGCompareExp node)
        {
            node.GetCompareExp().Apply(this);
            Utility.Emit(node.GetBoolEqualOrG().Text);
            node.GetAddExp().Apply(this);
        }

        #endregion

        #region add
        public override void CaseAAddAddExp(AAddAddExp node)
        {
            node.GetAddExp().Apply(this);
            Utility.Emit("+");
            node.GetMultExp().Apply(this);

        }
        public override void CaseASubAddExp(ASubAddExp node)
        {
            node.GetAddExp().Apply(this);
            Utility.Emit("-");
            node.GetMultExp().Apply(this);

        }
        #endregion

        #region mult
        public override void CaseAMultMultExp(AMultMultExp node)
        {
            node.GetMultExp().Apply(this);
            Utility.Emit("*");
            node.GetUnaryExp().Apply(this);
        }
        public override void CaseADivideMultExp(ADivideMultExp node)
        {
            node.GetMultExp().Apply(this);
            Utility.Emit("/");
            node.GetUnaryExp().Apply(this);
        }
        public override void CaseAModMultExp(AModMultExp node)
        {
            node.GetMultExp().Apply(this);
            Utility.Emit("%");
            node.GetUnaryExp().Apply(this);
        }
        #endregion

        #region unary
        public override void CaseANotUnaryExp(ANotUnaryExp node)
        {
            Utility.Emit(node.GetUnNot().Text);
            node.GetUnaryExp().Apply(this);
        }

        public override void CaseANegateUnaryExp(ANegateUnaryExp node)
        {
            Utility.Emit(node.GetArthSub().Text);
            node.GetUnaryExp().Apply(this);
        }
        public override void CaseAIncrementUnaryExp(AIncrementUnaryExp node)
        {
            Utility.Emit(node.GetUnIncrement().Text);
            node.GetUnaryExp().Apply(this);
        }
        public override void CaseADecrementUnaryExp(ADecrementUnaryExp node)
        {
            Utility.Emit(node.GetUnDecrement().Text);
            node.GetUnaryExp().Apply(this);
        }
        #endregion

        #region literals
        public override void CaseAIntScalar(AIntScalar node)
        {
            Utility.Emit(node.GetIntliteral().Text);
        }
        public override void CaseAFloatScalar(AFloatScalar node)
        {
            Utility.Emit(node.GetFloatliteral().Text);
        }
        public override void CaseABoolScalar(ABoolScalar node)
        {
            Utility.Emit(node.GetBooleanliteral().Text);
        }
        public override void CaseAStringScalar(AStringScalar node)
        {
            Utility.Emit(node.GetStringliteral().Text);
        }
        public override void CaseACharScalar(ACharScalar node)
        {
            Utility.Emit(node.GetCharliteral().Text);
        }
        public override void CaseACoordliteral(ACoordliteral node)
        {
            Utility.Emit("new Coord" + node.ToString().Trim());
        }
        public override void CaseAIdScalar(AIdScalar node)
        {
            Utility.Emit(node.GetId().Text);
        }

        #endregion

        public override void CaseAExpParenthScalar(AExpParenthScalar node)
        {
            if (node.BaseNode == "assign")
                node.GetExpression().Apply(this);
            else
            {
                Utility.Emit("(");
                node.GetExpression().Apply(this);
                Utility.Emit(")");
            }
        }

        public override void CaseAEntityMemberAccessScalar(AEntityMemberAccessScalar node)
        {
            string instance = node.GetTemplate().Text.Trim();
            string coordmember = node.GetMember().Text.Trim();
            if (coordmember.ToLower() == "x")
                Utility.Emit(instance + ".X");
            else if (coordmember.ToLower() == "y")
                Utility.Emit(instance + ".Y");
            else
                Utility.Emit(instance + "." + coordmember);
        }

        #region functionCall
        public override void CaseAEmptyFunctionCallScalar(AEmptyFunctionCallScalar node)
        {
            VariableEntry v = SemanticAnalyser.DrawFunctions.FirstOrDefault(i => i.Id == node.GetId().Text.Trim());

            if (SemanticAnalyser.DrawFunctions.Contains(v))
                Utility.Emit("Functions." + node.GetId().Text + node.GetOParenth().Text + node.GetCParenth().Text);
            else
                Utility.Emit(node.GetId().Text + node.GetOParenth().Text + node.GetCParenth().Text);
        }

        public override void CaseAFunctionCallScalar(AFunctionCallScalar node)
        {
            VariableEntry v = SemanticAnalyser.DrawFunctions.FirstOrDefault(i => i.Id == node.GetId().Text.Trim());
            if (SemanticAnalyser.DrawFunctions.Contains(v))
                Utility.Emit("Functions." + node.GetId().Text + node.GetOParenth().Text);
            else if ((SymbolTable.GetInstance().Find(node.GetId().Text.Trim(), scopes.Functions.ToString()) as VariableEntry).IsListFunction)
            {
                string id = node.GetId().Text.Trim().Replace("List", String.Empty);
                _operation = id;
                node.GetExpressionSequnece().Apply(this);
                return;
            }
            else
                Utility.Emit(node.GetId().Text + node.GetOParenth().Text);

            node.GetExpressionSequnece().Apply(this);
            Utility.Emit(node.GetCParenth().Text);
        }

        public override void CaseAMultipleExpressionSequnece(AMultipleExpressionSequnece node)
        {

            if (node.GetExpression().NodeType == "coord")
            {
                Utility.Emit("new Coord(");
                node.GetExpression().Apply(new CoordXCodeGen());
                Utility.Emit(",");
                node.GetExpression().Apply(new CoordYCodeGen());
                Utility.Emit(")");
                //Utility.Emit("C" + node.GetHashCode().ToString());
                Utility.Emit(",");
                node.GetExpressionSequnece().Apply(this);

            }
            else if (node.GetExpression().NodeType.Contains("List"))
            {
                switch (_operation)
                {
                    case "Get":
                        Utility.Emit(node.GetExpression().ToString().Trim() + "." + "Find(" + "i => i==" + node.GetExpressionSequnece().ToString().Trim() + ");");
                        break;

                    default:
                        Utility.Emit(node.GetExpression().ToString().Trim() + "." + _operation + "(");
                        Utility.Emit(node.GetExpressionSequnece().ToString().Trim() + ")");
                        break;
                }
            }
            else
            {
                node.GetExpression().Apply(this);
                Utility.Emit(node.GetComma().Text);
                node.GetExpressionSequnece().Apply(this);
            }
        }

        public override void CaseASingleExpressionSequnece(ASingleExpressionSequnece node)
        {
            if (node.GetExpression().NodeType == "coord")
            {
                Utility.Emit("new Coord(");
                node.GetExpression().Apply(new CoordXCodeGen());
                Utility.Emit(",");
                node.GetExpression().Apply(new CoordYCodeGen());
                Utility.Emit(")");
            }
            else if (_operation == "Count")

                Utility.Emit(node.GetExpression().ToString().Trim() + "." + _operation + ";");
            else
                node.GetExpression().Apply(this);
        }
        #endregion

    }

    public class CoordXCodeGen : DepthFirstAdapter
    {
        public override void CaseAAddAddExp(AAddAddExp node)
        {
            node.GetAddExp().Apply(this);
            Utility.Emit("+");
            node.GetMultExp().Apply(this);
        }
        public override void CaseASubAddExp(ASubAddExp node)
        {
            node.GetAddExp().Apply(this);
            Utility.Emit("-");
            node.GetMultExp().Apply(this);
        }

        public override void CaseADivideMultExp(ADivideMultExp node)
        {
            node.GetMultExp().Apply(this);
            Utility.Emit("/");
            node.GetUnaryExp().Apply(this);
        }

        public override void CaseAMultMultExp(AMultMultExp node)
        {
            node.GetMultExp().Apply(this);
            Utility.Emit("*");
            node.GetUnaryExp().Apply(this);
        }

        public override void CaseACoordliteral(ACoordliteral node)
        {
            node.GetLExpression().Apply(new ExpressionCodeGen());
        }

        public override void CaseAEntityMemberAccessScalar(AEntityMemberAccessScalar node)
        {
            Utility.Emit(node.GetTemplate().Text.Trim() + node.GetTDot().Text.Trim() + node.GetMember().Text.Trim() + ".X");
        }

        public override void CaseAIdScalar(AIdScalar node)
        {
            Utility.Emit(node.GetId().Text.Trim() + ".X");
        }

    }

    public class CoordYCodeGen : DepthFirstAdapter
    {
        public override void CaseAAddAddExp(AAddAddExp node)
        {
            node.GetAddExp().Apply(this);
            Utility.Emit("+");
            node.GetMultExp().Apply(this);
        }

        public override void CaseASubAddExp(ASubAddExp node)
        {
            node.GetAddExp().Apply(this);
            Utility.Emit("-");
            node.GetMultExp().Apply(this);
        }
        public override void CaseADivideMultExp(ADivideMultExp node)
        {
            node.GetMultExp().Apply(this);
            Utility.Emit("/");
            node.GetUnaryExp().Apply(this);
        }
        public override void CaseAMultMultExp(AMultMultExp node)
        {
            node.GetMultExp().Apply(this);
            Utility.Emit("*");
            node.GetUnaryExp().Apply(this);
        }
        public override void CaseACoordliteral(ACoordliteral node)
        {
            node.GetRExpression().Apply(new ExpressionCodeGen());
        }
        public override void CaseAIdScalar(AIdScalar node)
        {
            Utility.Emit(node.GetId().Text.Trim() + ".Y");
        }

        public override void CaseAEntityMemberAccessScalar(AEntityMemberAccessScalar node)
        {
            Utility.Emit(node.GetTemplate().Text.Trim() + node.GetTDot().Text.Trim() + node.GetMember().Text.Trim() + ".Y");
        }
    }
}
